package top.huxy.boot.controller.admin;

import com.github.pagehelper.PageInfo;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.connection.DataType;
import org.springframework.data.redis.core.RedisCallback;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import top.huxy.boot.annotation.Master;
import top.huxy.boot.annotation.NoRecordRequest;
import top.huxy.boot.annotation.Operation;
import top.huxy.boot.annotation.RequestRemake;
import top.huxy.boot.model.dto.Pager;
import top.huxy.boot.model.vo.VORedisData;
import top.huxy.boot.service.SystemService;
import top.huxy.boot.service.impl.MonitorServiceImpl;
import top.huxy.boot.util.StringUtils;
import top.huxy.boot.util.obj.MonitorInfoBean;
import top.huxy.boot.util.obj.Result;

import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicReference;
import java.util.stream.Collectors;

/**
 * @Classname SystemController
 * @Description SystemController
 * @Date 2021/9/27 19:22
 * @Created by huxy
 */
@RestController
@Slf4j
@RequestMapping("/admin/system")
public class SystemController {


    RedisTemplate<String, Object> redisTemplate;

    MonitorServiceImpl monitorService;

    SystemService systemService;

    public SystemController(RedisTemplate<String, Object> redisTemplate, MonitorServiceImpl monitorService, SystemService systemService) {
        this.redisTemplate = redisTemplate;
        this.monitorService = monitorService;
        this.systemService = systemService;
    }

    @Value("${huxy.app.upload.temp-file-path}")
    private String filePathTemp;

    @GetMapping("/hello")
    @NoRecordRequest
    public Result hello() {
        return Result.success("hello");
    }

    @GetMapping("/getRedisInfo")
    @RequestRemake("获取缓存信息")
    public Result getRedisInfo() {
        AtomicReference<Properties> info = new AtomicReference<>();
        redisTemplate.execute((RedisCallback<Object>) connection -> {
            //connection.info()可以获取redis的信息
            info.set(connection.info());
            return null;
        });
        return Result.success(info.get());
    }

    @Operation("重置Redis缓存")
    @RequestMapping("/clearRedisCache")
    @Master
    public Result clearRedisCache() {
        long time_start = System.currentTimeMillis();
        AtomicReference<Long> size = new AtomicReference<>();

        redisTemplate.execute((RedisCallback<Object>) connection -> {
            size.set(connection.dbSize());

            //清除DB
            connection.flushDb();
            return null;
        });

        long time_end = System.currentTimeMillis();

        String str = "清除成功，执行消耗" + (time_end - time_start) + "毫秒,清除了" + size.get() + "条记录";
        return Result.success(str);
    }

    /**
     * 获取redis缓存信息
     */
    @GetMapping("/getRedisData")
    @RequestRemake("获取redis信息")
    public Result getRedisData(Pager pager, @RequestParam(name = "filter", required = false) String filter) {
        Set<String> keys;
        //如果filter是null或空字符，就返回空列表
        if (filter == null || filter.trim().length() == 0) {
            keys = Collections.emptySet();
        } else {
            keys = redisTemplate.keys(filter);
        }

        if (keys == null) {
            return Result.success(null);
        }

        //总数
        int total = keys.size();

        //手动分页，获取key名
        List<String> list = keys
                .stream()
                .skip((pager.getPage() - 1) * pager.getPageSize())
                .limit(pager.getPageSize())
                .collect(Collectors.toList());

        List<VORedisData> res = new ArrayList<>();
        for (String s : list) {
            //获取key的过期时间
            Long expire = redisTemplate.getExpire(s, TimeUnit.SECONDS);

            //获取key的类型
            DataType rawType = redisTemplate.type(s);
            String type = rawType == null ? null : rawType.code();

            //获取key的数据
            byte[] dump = redisTemplate.dump(s);
            int size = 0;

            //设置key数据的大小
            if (dump != null) {
                size = dump.length;
            }

            //添加集合
            res.add(new VORedisData(s, expire, size, type));
        }

        //准备分页
        PageInfo<VORedisData> pageInfo = new PageInfo<>();

        //页码
        pageInfo.setPageNum(pager.getPage());

        //总数
        pageInfo.setTotal(total);

        //数据
        pageInfo.setList(res);

        //每页大小
        pageInfo.setPageSize(pager.getPageSize());

        return Result.success(pageInfo);
    }

    /**
     * 删除Redis的某个key
     */
    @Operation("删除Redis的Key")
    @GetMapping("/deleteRedisCache")
    public Result deleteRedisCache(@RequestParam("key") String key) {
        Boolean res = redisTemplate.delete(key);
        if (res != null && res) {
            return Result.success("删除成功");
        } else {
            return Result.clientError("删除失败" + res);
        }
    }

    @GetMapping("/getMemoryInfo")
    @RequestRemake("获取内存信息")
    public Result getMemoryInfo() {
        MonitorInfoBean monitorInfoBean = monitorService.getMonitorInfoBean();
        return Result.success(monitorInfoBean);
    }

    /**
     * 准备上传文件
     *
     * @return 返回一个token
     */
    @GetMapping("/prepareUpload")
    @RequestRemake("准备上传文件")
    @Master
    public Result prepareUpload(@RequestParam("fileSize") Long fileSize) {
        //最大上传文件限制
        if (fileSize > 1024 * 1024 * 100) {
            return Result.clientError("size so max : " + fileSize);
        }
        String token = StringUtils.unique();
        redisTemplate.opsForValue().set(token, fileSize, 10L, TimeUnit.MINUTES);
        return Result.success(token);
    }

    @NoRecordRequest
    @PostMapping("/uploadFile")
    @Master
    public Result uploadFile(MultipartFile file, @RequestParam("token") String token) throws IOException {
        Boolean hasKey = redisTemplate.hasKey(token);
        //判断token是否存在
        if (hasKey != null && hasKey) {
            if (file.isEmpty()) {
                return Result.clientError("file is empty");
            }

            //获取字节流
            byte[] bytes = file.getBytes();

            //redis中校验长度大小
            Long surplus = redisTemplate.opsForValue().increment(token, -bytes.length);

            //大小不符合，关闭上传通道
            if (surplus == null || surplus < 0) {
                redisTemplate.delete(token);
                if (!new File(filePathTemp + token).delete()) {
                    log.info("文件删除失败{}{}", filePathTemp, token);
                }

                return Result.clientError("与预期文件大小不符合:" + surplus);
            }

            //临时文件目录

            File distDir = new File(filePathTemp);
            if (!distDir.exists() && !distDir.mkdirs()) {
                log.warn("无法创建目录{},请自行创建", filePathTemp);
            }

            //写入临时文件。缓冲区5MB
            BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(filePathTemp + token, true), 1024 * 5);
            bos.write(bytes);
            bos.close();

            return Result.success(null);
        } else {
            return Result.clientError("token已过期或不存在");
        }
    }

    @GetMapping("/updateView")
    @Operation("更新View")
    @Master
    public Result updateView(@RequestParam("token") String token) {
        systemService.updateView(token);
        return Result.success("更新成功");
    }

    @GetMapping("/updateCMS")
    @Operation("更新CMS")
    @Master
    public Result updateCMS(@RequestParam("token") String token) {
        systemService.updateCMS(token);
        return Result.success("更新成功");
    }

    @Operation("更新服务")
    @GetMapping("/updateSystem")
    @Master
    public Result updateSystem(@RequestParam("token") String token) {
        systemService.updateSystem(token);
        return Result.success("更新成功。请等待服务器重启");
    }
}
